001 /*
002 * Copyright 2005 Stephen J. McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.tools.tasks;
020
021 import java.io.File;
022 import java.io.OutputStream;
023 import java.io.FileOutputStream;
024 import java.io.IOException;
025 import java.net.URI;
026 import java.util.ArrayList;
027 import java.util.List;
028
029 import net.dpml.lang.Classpath;
030 import net.dpml.lang.Category;
031 import net.dpml.lang.Info;
032 import net.dpml.lang.Part;
033 import net.dpml.lang.PartDecoder;
034
035 import net.dpml.library.Type;
036 import net.dpml.library.Resource;
037
038 import net.dpml.transit.Artifact;
039
040 import net.dpml.tools.Context;
041
042 import net.dpml.util.DefaultLogger;
043
044 import org.apache.tools.ant.Project;
045 import org.apache.tools.ant.BuildException;
046
047 import org.w3c.dom.Element;
048
049 /**
050 * Creation of an part definition in XML.
051 *
052 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
053 * @version 1.1.0
054 */
055 public class PartTask extends GenericTask
056 {
057 /**
058 * Constant artifact type for a plugin.
059 */
060 public static final String TYPE = "part";
061
062 /**
063 * Property key used to identify the plugin title.
064 */
065 public static final String PLUGIN_TITLE_KEY = "project.title";
066
067 /**
068 * Property key used to identify the plugin description.
069 */
070 public static final String PLUGIN_DESCRIPTION_KEY = "project.description";
071
072 /**
073 * Property key used to identify a custom plugin handler classname.
074 */
075 public static final String PLUGIN_HANDLER_KEY = "project.plugin.handler";
076
077 private boolean m_test = false;
078
079 private File m_output;
080
081 /**
082 * Set the test build policy. The default is to include
083 * the project artifact in the classpath of a created part, however - in a
084 * test scenario we don't want to do this. Setting test to true will result
085 * in the association of a local file uri to the project resource.
086 *
087 * @param test true if this is a local test part
088 */
089 public void setTest( boolean test )
090 {
091 m_test = test;
092 }
093
094 /**
095 * Override the default output destination.
096 *
097 * @param file the overriding destination
098 */
099 public void setDest( File file )
100 {
101 m_output = file;
102 }
103
104 /**
105 * Task execution.
106 */
107 public void execute()
108 {
109 Resource resource = getResource();
110 Part part = build( resource );
111 writePart( part );
112 }
113
114 /**
115 * Externalize the part as a deliverable.
116 * @param part the part to be externalized as XML
117 */
118 public void writePart( Part part )
119 {
120 File file = getOutputFile();
121 if( file.exists() )
122 {
123 try
124 {
125 Part existing = Part.load( file.toURI(), false );
126 if( part.equals( existing ) )
127 {
128 return;
129 }
130 }
131 catch( Exception e )
132 {
133 // continue
134 }
135 }
136
137 log( "Building part: " + file );
138 try
139 {
140 file.createNewFile();
141 final OutputStream output = new FileOutputStream( file );
142 try
143 {
144 part.encode( output );
145 checksum( file );
146 asc( file );
147 }
148 finally
149 {
150 try
151 {
152 output.close();
153 }
154 catch( IOException e )
155 {
156 e.printStackTrace();
157 }
158 }
159 }
160 catch( Exception e )
161 {
162 final String error =
163 "Part externalization error.";
164 throw new BuildException( error, e );
165 }
166 }
167
168 private File getOutputFile()
169 {
170 if( null != m_output )
171 {
172 return m_output;
173 }
174 else
175 {
176 Context context = getContext();
177 final String path = context.getLayoutFilename( TYPE );
178 final File deliverables = context.getTargetDeliverablesDirectory();
179 final File parts = new File( deliverables, TYPE + "s" );
180 final File file = new File( parts, path );
181 parts.mkdirs();
182 return file;
183 }
184 }
185
186 /**
187 * Build the part definition.
188 * @param resource the resource
189 * @return the part
190 */
191 protected Part build( Resource resource )
192 {
193 try
194 {
195 Info info = getInfo( resource );
196 Classpath classpath = getClasspath( resource );
197 Type type = resource.getType( TYPE );
198 Element element = type.getElement();
199 PartDecoder decoder = PartDecoder.getInstance();
200 DefaultLogger logger = new DefaultLogger( getTaskName() );
201 return decoder.build( logger, info, classpath, element, resource );
202 }
203 catch( Throwable e )
204 {
205 final String error =
206 "Internal error while attempting to build an external part definition."
207 + "\nResource: " + resource;
208 throw new BuildException( error, e, getLocation() );
209 }
210 }
211
212 /**
213 * Construct the info object based on properties declared by the
214 * supplied resource.
215 * @param resource the resource
216 * @return the info descriptor
217 */
218 protected Info getInfo( Resource resource )
219 {
220 Artifact artifact = resource.getArtifact( TYPE );
221 URI uri = artifact.toURI();
222 String title = resource.getInfo().getTitle();
223 String description = resource.getInfo().getDescription();
224 return new Info( uri, title, description );
225 }
226
227 private String getTitle( Resource resource )
228 {
229 return resource.getProperty( PLUGIN_TITLE_KEY );
230 }
231
232 private String getDescription( Resource resource )
233 {
234 return resource.getProperty( PLUGIN_DESCRIPTION_KEY );
235 }
236
237 /**
238 * Construct the classpath for the supplied resource.
239 * @param resource the resource
240 * @return the classpath
241 * @exception IOException is an IO error occurs
242 */
243 protected Classpath getClasspath( Resource resource ) throws IOException
244 {
245 URI[] sysUris = getURIs( resource, Category.SYSTEM );
246 URI[] publicUris = getURIs( resource, Category.PUBLIC );
247 URI[] protectedUris = getURIs( resource, Category.PROTECTED );
248 URI[] privateUris = getURIs( resource, Category.PRIVATE, true );
249 return new Classpath( sysUris, publicUris, protectedUris, privateUris );
250 }
251
252 private URI[] getURIs( Resource resource, Category category ) throws IOException
253 {
254 return getURIs( resource, category, false );
255 }
256
257 private URI[] getURIs( Resource resource, Category category, boolean self ) throws IOException
258 {
259 Resource[] resources = resource.getClasspathProviders( category );
260 ArrayList list = new ArrayList();
261 for( int i=0; i<resources.length; i++ )
262 {
263 Resource r = resources[i];
264 addURI( list, r );
265 }
266 if( self )
267 {
268 addURI( list, resource );
269 }
270 URI[] uris = (URI[]) list.toArray( new URI[0] );
271 return uris;
272 }
273
274 private void addURI( List list, Resource resource ) throws IOException
275 {
276 if( resource.isa( "jar" ) )
277 {
278 try
279 {
280 URI uri = toURI( resource );
281 list.add( uri );
282 }
283 catch( Exception e )
284 {
285 final String error =
286 "Unexpected error while attempting to resolve resource.";
287 IOException ioe = new IOException( error );
288 ioe.initCause( e );
289 throw ioe;
290 }
291 }
292 }
293
294 private URI toURI( Resource resource ) throws Exception
295 {
296 if( m_test && resource.equals( getResource() ) )
297 {
298 File local = getContext().getTargetDeliverable( "jar" );
299 return local.toURI();
300 }
301 else
302 {
303 return resource.getArtifact( "jar" ).toURI();
304 }
305 }
306
307 /**
308 * Get the project definition.
309 * @param project the project
310 * @return the build context
311 */
312 protected Context getContext( Project project )
313 {
314 Context context = (Context) project.getReference( "project.context" );
315 context.init();
316 return context;
317 }
318 }